package com.fubowen.lepton.ui.base

import android.os.Bundle
import android.view.View
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.lifecycleScope
import com.fubowen.lepton.conf.SiteConfigViewModel
import com.fubowen.lepton.dao.ThemeWithValueResult
import com.fubowen.lepton.extension.isNotNull
import com.fubowen.lepton.theme.ThemeViewModel
import kotlinx.coroutines.launch
import java.lang.Exception
import kotlin.reflect.KMutableProperty
import kotlin.reflect.jvm.isAccessible
import kotlin.reflect.jvm.javaType

open class ThemeFragment : Fragment() {
    private val mSiteViewModel: SiteConfigViewModel by activityViewModels()
    private val mThemeViewModel: ThemeViewModel by activityViewModels()
    private val mInject = mutableMapOf<String, KMutableProperty<*>>()
    var onChangedTheme: (ThemeWithValueResult) -> Unit = {}

    init {
        initInject()
    }

    //region onCreate
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        inject()
    }
    //endregion

    //region onViewCreated
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        observeTheme()
    }
    //endregion

    //region inject
    private fun initInject() {
        val members = this::class.members.filter { item -> item.annotations.any { it is Inject } }
        for (item in members) {
            val inject = item.annotations.filterIsInstance<Inject>().first()
            if (item is KMutableProperty<*>) {
                val name = inject.key.ifBlank { item.name }
                mInject[name] = item
            }
        }
    }

    private fun inject() {
        mSiteViewModel.settingCache.forEach {
            val prop = mInject[it.key]
            setPropertyValue(prop, it.value)
        }
    }

    private fun setPropertyValue(prop: KMutableProperty<*>?, value: String?) {
        if (prop == null) {
            return
        }
        if (value == null) {
            if (prop.returnType.isMarkedNullable) {
                prop.setter.call(this, null)
                return
            }
        } else {
            val newValue = when (prop.returnType.javaType) {
                Int::class.java -> value.toIntOrNull()
                Double::class.java -> value.toDoubleOrNull()
                String::class.java -> value
                Float::class.java -> value.toFloatOrNull()
                Boolean::class.java -> value.toBoolean()
                Long::class.java -> value.toLongOrNull()
                else -> throw Exception("不能识别的类型${prop.returnType}")
            }
            newValue?.let {
                prop.isAccessible = true
                prop.setter.call(this, it)
            }
        }
    }
    //endregion

    //region observeTheme
    private fun observeTheme() = lifecycleScope.launch {
        mThemeViewModel.themeFlow.collect {
            if (view.isNotNull()) {
                mThemeViewModel.setViewTheme(requireView())
                onChangedTheme(it)
            }
        }
    }
    //endregion

    fun themeViewModel() = mThemeViewModel
    fun checkOrSet(key: String, setFunc: (String) -> Unit) = mThemeViewModel.ifExistToCallback(key, setFunc)
}

@Target(AnnotationTarget.PROPERTY)
@Retention(AnnotationRetention.RUNTIME)
annotation class Inject(val key: String = "")


